package com.snmp.service.demo.snmpserver;

import com.snmp.service.demo.util.PropertiesUtil;
import org.apache.log4j.BasicConfigurator;
import org.snmp4j.MessageDispatcher;
import org.snmp4j.MessageDispatcherImpl;
import org.snmp4j.TransportMapping;
import org.snmp4j.agent.*;
import org.snmp4j.agent.cfg.EngineBootsCounterFile;
import org.snmp4j.agent.io.DefaultMOPersistenceProvider;
import org.snmp4j.agent.io.MOInput;
import org.snmp4j.agent.io.MOInputFactory;
import org.snmp4j.agent.io.prop.PropertyMOInput;
import org.snmp4j.agent.mo.MOAccessImpl;
import org.snmp4j.agent.mo.util.VariableProvider;
import org.snmp4j.agent.request.Request;
import org.snmp4j.agent.request.RequestStatus;
import org.snmp4j.agent.request.SubRequest;
import org.snmp4j.agent.request.SubRequestIterator;
import org.snmp4j.log.Log4jLogFactory;
import org.snmp4j.log.LogAdapter;
import org.snmp4j.log.LogFactory;
import org.snmp4j.log.LogLevel;
import org.snmp4j.mp.MPv3;
import org.snmp4j.security.SecurityProtocols;
import org.snmp4j.smi.*;
import org.snmp4j.transport.TransportMappings;
import org.snmp4j.util.ThreadPool;
import org.springframework.core.io.ClassPathResource;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;


import javax.annotation.PostConstruct;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.concurrent.ThreadLocalRandom;


/**
 * @author hch
 * @创建人 hch
 * @创建时间 2021/4/26 11:46
 * @描述 TODO
 * @版本 1.0.0
 */
@Component
public class SNMP4JAgent implements VariableProvider {

    static {
        LogFactory.setLogFactory(new Log4jLogFactory());
        BasicConfigurator.configure();
        //修改日志级别
        LogFactory.getLogFactory().getRootLogger().setLogLevel(LogLevel.ERROR);
    }

    private LogAdapter logger = LogFactory.getLogger(SNMP4JAgent.class);

    protected AgentConfigManager agent;
    protected DefaultMOServer server;
    private String configFile;
    private File bootCounterFile;

    public SNMP4JAgent() {
        configFile = "configFile.cfg";
        bootCounterFile = new File("bootCounterFile.bc");

        String udp = "udp:0.0.0.0/161";
        server = new DefaultMOServer();

        MOServer[] moServers = new MOServer[]{server};

        final Properties props = new Properties();
        try {
            ClassPathResource classPathResource = new ClassPathResource("SampleAgentConfig.properties");
            InputStream inputStream = classPathResource.getInputStream();
            props.load(inputStream);
        } catch (IOException ex) {
            ex.printStackTrace();
        }
        MOInputFactory configurationFactory = new MOInputFactory() {
            @Override
            public MOInput createMOInput() {
                return new PropertyMOInput(props, SNMP4JAgent.this);
            }
        };

        MessageDispatcher messageDispatcher = new MessageDispatcherImpl();
        List<String> list = new ArrayList<>();
        list.add(udp);
        addListenAddresses(messageDispatcher, list);

        agent = new AgentConfigManager(
                new OctetString(MPv3.createLocalEngineID()),
                messageDispatcher,
                null,
                moServers,
                ThreadPool.create("snmp4JAgent", 10),
                configurationFactory,
                new DefaultMOPersistenceProvider(moServers, configFile),
                new EngineBootsCounterFile(bootCounterFile)
        );
    }


    @Override
    public Variable getVariable(String name) {
        OID oid;
        OctetString context = null;
        int pos = name.indexOf(':');
        if (pos >= 0) {
            context = new OctetString(name.substring(0, pos));
            oid = new OID(name.substring(pos + 1, name.length()));
        } else {
            oid = new OID(name);
        }
        final DefaultMOContextScope scope = new DefaultMOContextScope(context,
                oid, true, oid, true);
        MOQuery query = new MOQueryWithSource(scope, false, this);
        ManagedObject mo = server.lookup(query);
        if (mo != null) {
            final VariableBinding vb = new VariableBinding(oid);
            final RequestStatus status = new RequestStatus();
            SubRequest req = new SubRequest() {
                private boolean completed;
                private MOQuery query;

                @Override
                public boolean hasError() {
                    return false;
                }

                @Override
                public void setErrorStatus(int errorStatus) {
                    status.setErrorStatus(errorStatus);
                }

                @Override
                public int getErrorStatus() {
                    return status.getErrorStatus();
                }

                @Override
                public RequestStatus getStatus() {
                    return status;
                }

                @Override
                public MOScope getScope() {
                    return scope;
                }

                @Override
                public VariableBinding getVariableBinding() {
                    return vb;
                }

                @Override
                public Request getRequest() {
                    return null;
                }

                @Override
                public Object getUndoValue() {
                    return null;
                }

                @Override
                public void setUndoValue(Object undoInformation) {
                }

                @Override
                public void completed() {
                    completed = true;
                }

                @Override
                public boolean isComplete() {
                    return completed;
                }

                @Override
                public void setTargetMO(ManagedObject managedObject) {

                }

                @Override
                public ManagedObject getTargetMO() {
                    return null;
                }

                @Override
                public int getIndex() {
                    return 0;
                }

                @Override
                public void setQuery(MOQuery query) {
                    this.query = query;
                }

                @Override
                public MOQuery getQuery() {
                    return query;
                }

                @Override
                public SubRequestIterator repetitions() {
                    return null;
                }

                @Override
                public void updateNextRepetition() {
                }

                @Override
                public Object getUserObject() {
                    return null;
                }

                @Override
                public void setUserObject(Object userObject) {

                }

            };
            mo.get(req);
            return vb.getVariable();
        }
        return null;
    }

    protected void addListenAddresses(MessageDispatcher md,
                                      List<String> addresses) {
        for (String addressString : addresses) {
            Address address = GenericAddress.parse(addressString);
            if (address == null) {
                logger.fatal("Could not parse address string '" + addressString
                        + "'");
                return;
            }
            @SuppressWarnings("rawtypes")
            TransportMapping tm = TransportMappings.getInstance().createTransportMapping(address);
            if (tm != null) {
                md.addTransportMapping(tm);
            } else {
                logger.warn("No transport mapping available for address '"
                        + address + "'.");
            }
        }
    }


    private static Map<String, String> oidValueMap = null;

    public void registerMIBs() {
        try {
            server.register(new SysDate(), null);
            server.register(new SysInitDevice(), null);
            server.register(new SysInitTest(), null);
            // server.register(new SysInitTestT1(), null);
            // server.register(new SysInitTestT2(), null);
            // server.register(new SysInitTestT11(), null);
            // server.register(new SysInitTestT22(), null);
            //温湿度传感器 组件 根oid 1.3.6.1.4.1.123456.1.1
            //电压电流传感器 组件 根oid 1.3.6.1.4.1.123456.1.2
            //实例
            /**
             温湿度传感器01 序列号   1
             温湿度传感器02  序列号  2
             //信号指标
             1: 温度
             2: 湿度

             温湿度传感器01 温度指标 oid : 1.3.6.1.4.1.123456.1.1.1.1.0
             温湿度传感器01 湿度指标 oid : 1.3.6.1.4.1.123456.1.1.1.2.0

             温湿度传感器02 温度指标 oid : 1.3.6.1.4.1.123456.1.1.2.1.0
             温湿度传感器02 湿度指标 oid : 1.3.6.1.4.1.123456.1.1.2.2.0


             电压电流传感器01 序列号   1
             电压电流传感器02  序列号  2
             信号指标  电压:1
             电流 :2

             电压电流传感器01 电压指标 oid : 1.3.6.1.4.1.123456.1.2.1.1.0
             电压电流传感器01 电流指标 oid : 1.3.6.1.4.1.123456.1.2.1.2.0

             电压电流传感器02 电压指标 oid : 1.3.6.1.4.1.123456.1.2.2.1.0
             电压电流传感器02 电流指标 oid : 1.3.6.1.4.1.123456.1.2.2.2.0
             **/
            try {
                String path = System.getProperty("user.dir") + "/src/main/resources/oidValue.properties";
                oidValueMap = PropertiesUtil.getAllProperties(path);
                logger.info("oidValueMap:" + oidValueMap);

                if (!CollectionUtils.isEmpty(oidValueMap)) {
                    oidValueMap.forEach((key, value) -> {
                        try {
                            if (value.contains(",")) {
                                String[] split = value.split(",");
                                value = split[0];
                            }
                            server.register(new SysInitTestTA(key, MOAccessImpl.ACCESS_READ_WRITE, value), null);
                        } catch (DuplicateRegistrationException e) {
                            logger.error("DuplicateRegistrationException: " + e);
                        }
                    });
                }

            } catch (Exception e) {
                logger.error("oidValue.properties配置文件没有找到.");
            }


//            server.register(new SysInitTestTA("1.3.6.1.4.1.123456.1.1.1.1.0", MOAccessImpl.ACCESS_READ_WRITE, "28.3"), null);
//            server.register(new SysInitTestTA("1.3.6.1.4.1.123456.1.1.1.2.0", MOAccessImpl.ACCESS_READ_WRITE, "58.5"), null);
//            server.register(new SysInitTestTA("1.3.6.1.4.1.123456.1.1.2.1.0", MOAccessImpl.ACCESS_READ_WRITE, "27.6"), null);
//            server.register(new SysInitTestTA("1.3.6.1.4.1.123456.1.1.2.2.0", MOAccessImpl.ACCESS_READ_WRITE, "57.3"), null);
//            server.register(new SysInitTestTA("1.3.6.1.4.1.123456.1.2.1.1.0", MOAccessImpl.ACCESS_READ_WRITE, "5.0"), null);
//            server.register(new SysInitTestTA("1.3.6.1.4.1.123456.1.2.1.2.0", MOAccessImpl.ACCESS_READ_WRITE, "220.0"), null);
//            server.register(new SysInitTestTA("1.3.6.1.4.1.123456.1.2.2.1.0", MOAccessImpl.ACCESS_READ_WRITE, "5.1"), null);
//            server.register(new SysInitTestTA("1.3.6.1.4.1.123456.1.2.2.2.0", MOAccessImpl.ACCESS_READ_WRITE, "220.2"), null);

            server.register(MoTableUtil.getEmuTable(), null);
            server.register(MoTableUtil.getEdfaTable(), null);

            MoTableUtil.fillDefaultDataIntoEmuTable();
            MoTableUtil.fillDefaultDataIntoEdfaTable();
        } catch (DuplicateRegistrationException e) {
            e.printStackTrace();
        }

    }

    private static Random random = new Random(System.currentTimeMillis());

    /**
     * 生成1到100的随机数
     *
     * @return
     */
    private int getRandValue() {
        return ThreadLocalRandom.current().nextInt(1, 100);
    }


    /**
     * 枚举随机数
     *
     * @return
     */
    private int getRandValueEnum(int length) {
        return ThreadLocalRandom.current().nextInt(length);
    }


    @PostConstruct
    public void run() {
        try {
            agent.initialize();
            registerMIBs();
            agent.setupProxyForwarder();
            agent.setTableSizeLimits(new Properties());
            agent.run();
        } catch (Exception e) {
            logger.error("snmp 服务端运行出现错误: " + e);
        }


    }


    public static void main(String[] args) {


        SNMP4JAgent sa = new SNMP4JAgent();
        SecurityProtocols.getInstance().addDefaultProtocols();
        sa.run();
    }


    /**
     * 使得测试数据发生变化
     * 每 13  秒钟 执行一次
     */
    @Scheduled(cron = "0 */2 * * * ?")
    public void updateOidValueTask() {
        // logger.info("oidValueMap: 数据准备开始更新");
        if (!CollectionUtils.isEmpty(oidValueMap)) {
            oidValueMap.forEach((key, value) -> {
                if (value.contains(",")) {
                    String[] split = value.split(",");
                    value = split[getRandValueEnum(split.length)];
                } else {
                    int randValue = getRandValue();
                    double v = Double.parseDouble(value);
                    v += randValue;
                    value = String.valueOf(v);
                }


                OID oid = new OID(key);
                OctetString octetString = new OctetString(value);
                VariableBinding variableBinding = new VariableBinding(oid, octetString);
                DefaultMOServer.setValue(server, octetString, variableBinding);
            });
        }

        //  logger.info("oidValueMap: 数据更新完成！");


    }
}
